// SPDX-License-Identifier: GPL-2.0 or GPL-3.0
// Copyright © 2019 Ariadne Devos
// sHT -- compare byte arrays

#ifndef _sHT_STRING_H
#define _sHT_STRING_H

#include <sHT/test.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>

#include <sHT/minmax-arch.h>

/** String comparison

  In short: <string.h>, but with Spectre precautions.
  -- and non-optimised, but profile first before fixing that.
  These functions interpret memory areas as a sequence of readable
  bytes, test them and perhaps calculate something. */

/** Compute the minimum of two integers, typically some kind of length.

  Ensures:
  (x) ret = x ∨ ret = y
  (y) ret ≤ x
  (z) ret ≤ y */
__attribute__((const))
static inline size_t
sHT_min_size(size_t x, size_t y)
{
	_sHT_min(&x, y);
	return x;
}

/** Compare two buffers of know, equal lengths for equality.

  The semantics are the same as @var{sHT_streq}. @var{length0} and
  @var{length1} have been unified into @var{length}. */
__attribute__((pure))
size_t
sHT_memeq(const char *buffer0, const char *buffer1, size_t correct, size_t otherwise, size_t length);

/** Compare two buffers of known length for equality

  @var{buffer0}: the first string, not modified concurrently and readable
  @var{buffer1}: the second string, not modified concurrently and readable
  @var{correct}: return if the strings are equal
  @var{otherwise}: return if the strings are unequal
  @var{length0}: the number of bytes in @var{buffer0}
  @var{length1}: the number of bytes in @var{buffer1}

  A little Spectre bug: even if the length is zero, the first element might be
  read. A timing side-channel: the execution time does not only depend on the
  lengths, but also the characters of the strings -- therefore, unsuitable for
  passphrases and their hashes.

  The test may speculatively be incorrect, but the return value is always one
  of @var{correct} or @var{otherwise}. */
__attribute__((pure))
static inline size_t
sHT_streq(const char *buffer0, const char *buffer1, size_t correct, size_t otherwise, size_t length0, size_t length1)
{
	if (sHT_neq(length0, length1))
		return otherwise;
	return sHT_memeq(buffer0, buffer1, correct, otherwise, sHT_min_size(length0, length1));
}

/** Move bytes from @var{from} to the end of @var{to}

  @var{to}: a writable buffer that is not accessed concurrently
  @var{from}: a readable buffer that is not modified concurrently
  `length0`:
    the capacity, size of `to` (positive)
  `length1`:
    the capacity, size of `from` (positive)
  @var{i0}: the index in @var{to} to start writing to
    (not greater than length0)
  @var{i1}: the index in @var{from} to start reading from
    (not greater than length1)

  @var{to} and @var{from} are disjoint.
  All bytes of @var{to} may be written and
  all bytes of @var{from} may be read.

  Non-speculatively, copy as many bytes as the capacities allow,
  in order, contiguously, from @var{from + i1} to @var{to + i0},
  and return the number of moved bytes.

  Speculatively, less bytes could be copied, but the number of
  moved bytes is still correct. */
size_t
sHT_append(char *to, const char *from, size_t length0, size_t length1, size_t i0, size_t i1);

#endif
